#include "mesh.hpp"

namespace hms {

/**
 * @brief Calculates the rowwise average of vertices in a mesh. Which vertices
 * to average can be specified by the second parameter.
 * @overload
 * @param mesh parent mesh that holds the vertex coefficients
 * @param vertices the vertex indices
 */
template<typename Indices>
Vector2s vectorAverage( const Mesh& mesh, const Indices& vertices ){
	return vectorAverage( mesh.vertices()( hms::all, vertices ) );
}


/**
 * @overload
 */
template<typename MeshType, typename Indices>
scalar verticesToArea(const MeshBase<MeshType>& mesh, const Indices& vertices){
	/* faster version using indexed view, if member function vertices() is
	 * available */
	if constexpr(
		std::is_member_function_pointer_v<decltype(&MeshType::vertices)>
	){
		return verticesToArea(
			mesh.derived().vertices()(hms::all, vertices)
		);
	} else {
	/* fallback option */
		Matrix2Xs coords ( vertices.size() );
		for ( Index i{0}; i<coords.cols(); ++i)
			coords.col(i) = mesh.vertex(i);
		return verticesToArea( coords );
	}
}



/**
 * @overload
 */
template<typename MeshType, typename Indices>
Vector2s verticesToCentroid(
	const MeshBase<MeshType>& mesh, const Indices& vertices, scalar area
){
	/* faster version using indexed view, if member function vertices() is
	 * available */
	if constexpr(
		std::is_member_function_pointer_v<decltype(&MeshType::vertices)>
	){
		return verticesToCentroid(
			mesh.derived().vertices()(hms::all, vertices), area
		);
	} else {
	/* fallback option */
		Matrix2Xs coords ( vertices.size() );
		for ( Index i{0}; i<coords.cols(); ++i)
			coords.col(i) = mesh.vertex(i);
		return verticesToCentroid( coords, area );
	}
}

/**
 * @brief Returns the intersection point of a line through the vertices of an
 * edge with the line through the centroids of the adjacent cells.
 * 
 * @tparam EdgeType 
 * @param edge 
 * @return Vector2s 
 */
template<typename EdgeType>
Vector2s intersectionWithCentroids( const EdgeBase<EdgeType>& edge, Index order)
{
	return 
		Line::Through(
			edge.mesh().vertex(edge.vertices()[0]),
			edge.mesh().vertex(edge.vertices()[1])
		).intersection(
		Line::Through(
			edge.mesh().cell(edge.cells(order)[0]).centroid(),
			edge.mesh().cell(edge.cells(order)[1]).centroid() )
		);
}

/**
 * @brief Returns true if the line through the points A and B intersects the
 * edge between its endpoints and false otherwise.
 * 
 * @tparam EdgeType 
 * @param A 
 * @param B 
 * @param edge 
 * @return true 
 * @return false 
 */
template<typename EdgeType>
bool intersects(
	const Ref<const Vector2s>& A,
	const Ref<const Vector2s>& B,
	const EdgeBase<EdgeType>& edge
){
	return intersects(
		A,
		B,
		edge.mesh().vertex(edge.vertices()[0]),
		edge.mesh().vertex(edge.vertices()[1])
	);
}



template<typename EdgeType>
Vector2s mirrorAlongEdge(
	const EdgeBase<EdgeType>& edge,
	const Ref<const Vector2s>& p
){
	return mirrorAlongLine(
		ParamLine::Through(
			edge.mesh().vertex(edge.vertices()[0]),
			edge.mesh().vertex(edge.vertices()[1])
		),
		p
	);
}


template<typename EdgeType>
Vector2s transformCoords(
	const Ref<const Vector2s>& v, const EdgeBase<EdgeType>& edge
){
	return transformCoords( v, edge.normal() );
}

template<typename EdgeType>
Vector2s retransformCoords(
	const Ref<const Vector2s>& v, const EdgeBase<EdgeType>& edge
){
	return retransformCoords( v, edge.normal() );
}

}